package ch.ethz.fcl.metrobuzz.data.ltt;

import java.util.ArrayList;
import java.util.List;

import ch.ethz.fcl.metrobuzz.data.MBData;
import ch.ethz.fcl.metrobuzz.data.ltt.link.FrequentLinkGraph;
import ch.ethz.fcl.metrobuzz.data.ltt.link.LTTLink;
import ch.ethz.fcl.metrobuzz.data.ltt.node.LTTNode;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Edge;
import ch.ethz.fcl.metrobuzz.render.interchange.inter.InterInterchangeRender;

public class FrequentLTT {
	private TrajectoryGraph trajGraph;

	private List<Integer> selectedTrajIndex;

	private List<LongTermTrajectory> visitedTrajs;
	private List<LongTermTrajectory> frequentTrajs;

	private List<LongTermTrajectory> selectedNodeTraj;

	private List<LTTNode> frequentLTTNodes;
	private List<LTTLink> frequentLTTLinks;

	private FrequentLinkGraph flg = null;

	public FrequentLTT(MBData data) {
		this.trajGraph = data.getTrajGraph();
	}

	public void findFrequentLTT(LTTNode selectedNode) {
		long startTime = System.currentTimeMillis();

//		allTrajs = trajGraph.getAllTrajList();
//		selectedTrajIndex = trajGraph.getSelectedTrajIndexList();

		visitedTrajs = new ArrayList<LongTermTrajectory>();
		frequentTrajs = new ArrayList<LongTermTrajectory>();
		selectedNodeTraj = new ArrayList<LongTermTrajectory>();

		LongTermTrajectory start = new LongTermTrajectory();
		start.addNode(selectedNode, 0);
		for (int i = 0; i < selectedTrajIndex.size(); i++)
			start.addTrajIndex(selectedTrajIndex.get(i));

		expandFrequentItemSet(selectedNode, start);
		float timeCost = (System.currentTimeMillis() - startTime) / 1000.0f;

		removeDuplicateTransaction(frequentTrajs);
		findFrequentLTTNodesAndLinks();

		 flg = new FrequentLinkGraph(trajGraph, frequentTrajs,
		 frequentLTTLinks);
		
		 flg.processFrequentLink();

		if (trajGraph.DEBUG) {
			System.out.println("\nFound " + frequentTrajs.size()
					+ " frequent items sets in " + timeCost
					+ " seconds, they are ");
			// for (int i = 0; i < frequentTrajs.size(); i++)
			// System.out.println(frequentTrajs.get(i).toString());
			//
			// System.out.println("\nFound " + frequentLTTNodes.size()
			// + " LTT nodes, they are:");
			// for (LTTNode node : frequentLTTNodes)
			// System.out.print(node.toNode().getId() + " ");
			//
			// System.out.println("\nFound " + frequentLTTLinks.size()
			// + " LTT links, they are:");
			// for (LTTLink link : frequentLTTLinks)
			// System.out.print(link.toString() + "; ");
			// System.out.println();
		}
	}

	private void expandFrequentItemSet(LTTNode selectedNode,
			LongTermTrajectory ltt) {
		LTTNode firstNode = ltt.getNodeList().get(0);

		List<Edge> incomingEdges = firstNode.toNode().getInEdges();
		for (int j = 0; j < incomingEdges.size(); j++) {
			int startNodeId = incomingEdges.get(j).getStartNodeIndex();
			LTTNode preNode = trajGraph.getSelectedLTTNodeMap()
					.get(startNodeId);
			if (preNode == null)
				continue;

			if (!ltt.getNodeList().contains(preNode)) {
				LongTermTrajectory newTL = new LongTermTrajectory(
						ltt.getNodeList());
				newTL.addNode(preNode, 0);
				if (visitedTrajs.contains(newTL))
					continue;
				for (int k = 0; k < ltt.getTrajIndexList().size(); k++) {
					Integer trajIndex = ltt.getTrajIndexList().get(k);
					Float passTime = preNode.toNode().getTrajPassTimeMap()
							.get(trajIndex);

					// this trajectory also pass the previous node
					if (passTime != null)
						newTL.addTrajIndex(ltt.getTrajIndexList().get(k));
				}
				visitedTrajs.add(newTL);

				if (newTL.getNodeList().size() == 2
						&& (newTL.getNodeList().get(0).equals(selectedNode) || newTL
								.getNodeList().get(1).equals(selectedNode)))
					selectedNodeTraj.add(newTL);

				if (newTL.frequency() > selectedTrajIndex.size()
						* InterInterchangeRender.supportRatio) {
					frequentTrajs.add(newTL);
					frequentTrajs.remove(ltt);
					expandFrequentItemSet(selectedNode, newTL);
				}
			}
		}

		LTTNode lastNode = ltt.getNodeList().get(ltt.nodeSize() - 1);
		List<Edge> outgoingEdges = lastNode.toNode().getOutEdges();

		for (int j = 0; j < outgoingEdges.size(); j++) {
			int endNodeId = outgoingEdges.get(j).getEndNodeIndex();
			LTTNode afterNode = trajGraph.getSelectedLTTNodeMap()
					.get(endNodeId);
			if (afterNode == null)
				continue;
			if (!ltt.getNodeList().contains(afterNode)) {
				LongTermTrajectory newTL = new LongTermTrajectory(
						ltt.getNodeList());
				newTL.addNode(afterNode, newTL.nodeSize());
				if (visitedTrajs.contains(newTL))
					continue;
				for (int k = 0; k < ltt.getTrajIndexList().size(); k++) {
					Integer trajIndex = ltt.getTrajIndexList().get(k);
					Float passTime = afterNode.toNode().getTrajPassTimeMap()
							.get(trajIndex);

					// this trajectory also pass the after node
					if (passTime != null)
						newTL.addTrajIndex(ltt.getTrajIndexList().get(k));
				}
				visitedTrajs.add(newTL);

				if (newTL.getNodeList().size() == 2
						&& (newTL.getNodeList().get(0).equals(selectedNode) || newTL
								.getNodeList().get(1).equals(selectedNode)))
					selectedNodeTraj.add(newTL);

				if (newTL.frequency() > selectedTrajIndex.size()
						* InterInterchangeRender.supportRatio) {
					frequentTrajs.add(newTL);
					frequentTrajs.remove(ltt);
					expandFrequentItemSet(selectedNode, newTL);
				}
			}
		}
	}

	private void removeDuplicateTransaction(List<LongTermTrajectory> transList) {
		List<LongTermTrajectory> shortTransList = new ArrayList<LongTermTrajectory>();
		for (LongTermTrajectory trans : transList)
			for (LongTermTrajectory otherTrans : transList) {
				if (trans != otherTrans && trans.contains(otherTrans))
					shortTransList.add(otherTrans);
			}
		transList.removeAll(shortTransList);
	}

	private void findFrequentLTTNodesAndLinks() {
		frequentLTTNodes = new ArrayList<LTTNode>();

		for (LongTermTrajectory ltt : frequentTrajs)
			for (LTTNode node : ltt.getNodeList())
				if (!frequentLTTNodes.contains(node))
					frequentLTTNodes.add(node);

		frequentLTTLinks = new ArrayList<LTTLink>();
		for (LongTermTrajectory ltt : frequentTrajs) {
			List<LTTNode> nodeList = ltt.getNodeList();
			for (int i = 0; i < nodeList.size() - 1; i++) {
				LTTNode from = nodeList.get(i);
				LTTNode to = nodeList.get(i + 1);
				LTTLink link = new LTTLink(trajGraph, from, to);
				if (!frequentLTTLinks.contains(link))
					frequentLTTLinks.add(link);
			}
		}

		for (int j = 0; j < selectedTrajIndex.size(); j++) {
//			Integer trajIndex = selectedTrajIndex.get(j);
//			SingleTrajectory traj = allTrajs.get(trajIndex);

//			List<Integer> nodeIndices = traj.getNodeIndices();
//			for (int i = 0; i < nodeIndices.size() - 1; i++) {
//				LTTNode from = trajGraph.getSelectedLTTNodeMap().get(
//						nodeIndices.get(i));
//				LTTNode to = trajGraph.getSelectedLTTNodeMap().get(
//						nodeIndices.get(i + 1));
//
//				LTTLink link = new LTTLink(trajGraph, from, to);
//				int index = frequentLTTLinks.indexOf(link);
//				if (index != -1)
//					frequentLTTLinks.get(index).addPassTrajIndex(trajIndex);
//			}
		}
	}

	public FrequentLinkGraph getFrequentLinkGraph() {
		return flg;
	}

	public List<LongTermTrajectory> getSelectedNodeTrans() {
		return selectedNodeTraj;
	}

	public List<LTTNode> getFrequentNodes() {
		return frequentLTTNodes;
	}

	public List<LTTLink> getFrequentLinks() {
		return frequentLTTLinks;
	}
}
